package com.chenfan.mcn.utils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * @Author chenguopeng
 * @Date 2021/6/7 16:24
 */
@Slf4j
public class CommonUtil {

    /**
     * 签名Key
     */
    private static final String SIGN_KEY = "chenfan-mcn";

    /**
     * 字符数字数组
     */
    public static String[] CHARS = new String[] { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
            "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8",
            "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
            "U", "V", "W", "X", "Y", "Z"};

    public static String REGX="%";

    /**
     * list转string
     *
     * @param list
     * @return
     */
    public static String listToString(List<String> list, char separator) {
        return StringUtils.join(list, separator);
    }

    /**
     * 默认HmacMD5签名
     *
     * @param valueToDigest
     * @return
     */
    public static String hmacMd5Hex(String valueToDigest) {
        return hmacMd5Hex(SIGN_KEY, valueToDigest);
    }

    /**
     * HmacMD5签名
     *
     * @param key
     * @param valueToDigest
     * @return
     */
    public static String hmacMd5Hex(String key, String valueToDigest) {
        try {
            SecretKey secretkey = new SecretKeySpec(key.getBytes(), "HmacMD5");
            Mac mac = Mac.getInstance(secretkey.getAlgorithm());
            mac.init(secretkey);
            byte[] code = mac.doFinal(valueToDigest.getBytes());
            return Hex.encodeHexString(code);
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalArgumentException(e);
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException(e);
        }
    }


    /**
     * 按照对象的属性，对对象列表进行去重
     *
     * @param keyExtractor {@link Function}
     * @param <T>          {@link Predicate}
     * @return {@link T}
     */
    public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>(16);
        System.out.println("这个函数将应用到每一个item");
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }


    /**
     * 拼接年月
     *
     * @param aeQuarterlyCode {@link String}
     * @return {@link String}
     */
    public static String getAeQuarterlyCode(String aeQuarterlyCode) {
        String year = aeQuarterlyCode.substring(0, 4);
        String month = aeQuarterlyCode.substring(4, 6);
        return year + "年" + month + "月";
    }

    public static String defaultIfEmpty(Object object) {
        return defaultIfEmpty(object, "");
    }

    public static String defaultIfEmpty(Object object, String defaultValue) {
        if (object == null) {
            return defaultValue;
        }
        return object.toString();
    }

    public static String defaultString(JSONObject jsonObject, Object value) {
        if (MapUtils.isEmpty(jsonObject) || value == null) {
            return "";
        }
        return StringUtils.defaultString(jsonObject.getString(value.toString()), "");
    }

    public static boolean isEmptyArray(String arrayStr) {
        if (StringUtils.isBlank(arrayStr)) {
            return true;
        }
        JSONArray jsonArray = JSONObject.parseArray(arrayStr);
        return jsonArray.isEmpty();
    }

    public static boolean isNotEmptyArray(String arrayStr) {
        return !isEmptyArray(arrayStr);
    }

    public static String yesOrNo(Integer value) {
        if (value != null && value.intValue() == 1) {
            return "是";
        }
        if (value != null && value.intValue() == 0) {
            return "否";
        }
        return null;
    }

    public static Field[] getAllDeclaredFields(Class<?> clazz) {
        Assert.notNull(clazz, "Class must not be null");
        Field[] fields = clazz.getDeclaredFields();
        Class<?> searchType = clazz.getSuperclass();
        while (Object.class != searchType && searchType != null) {
            fields = ArrayUtils.addAll(fields,searchType.getDeclaredFields());
            searchType = searchType.getSuperclass();
        }
        return fields;
    }

    public static void clearList(List<?>... listArray) {
        if(ArrayUtils.isEmpty(listArray)) {
            return;
        }
        for(List<?> list : listArray) {
            if(CollectionUtils.isNotEmpty(list)) {
                list.clear();
            }
        }
    }

    public static List<Long> getTargetList(List<Long> sourceList, List<Long> joinList) {
        if(CollectionUtils.isEmpty(sourceList)) {
            if(CollectionUtils.isNotEmpty(joinList)) {
                return new ArrayList<>(joinList);
            }
            return null;
        }
        List<Long> targetList = new ArrayList<>(sourceList);
        if(CollectionUtils.isNotEmpty(joinList)) {
            for(Long value : joinList) {
                if(!targetList.contains(value)){
                    targetList.add(value);
                }
            }
        }
        return targetList;
    }

    public static boolean anyEqualsIgnoreCase(String[] targetArray, String... sources) {
        if(ArrayUtils.isEmpty(targetArray) || ArrayUtils.isEmpty(sources)) {
            return false;
        }
        for(String source : sources) {
            if(StringUtils.equalsAnyIgnoreCase(source, targetArray)) {
                return true;
            }
        }
        return false;
    }

    public static <T> List<T> copyList(List<T> sourceList) {
        if(CollectionUtils.isEmpty(sourceList)) {
            return null;
        }
        List<T> tempList = new ArrayList<>(sourceList.size());
        tempList.addAll(sourceList);
        return tempList;
    }

    public static <T> List<T> defaultList(List<T> sourceList) {
        if(Objects.isNull(sourceList)) {
            return new ArrayList<>(0);
        }
        return sourceList;
    }

    public static <T> List<T> defaultList(List<T> sourceList, T defaultValue) {
        if(CollectionUtils.isEmpty(sourceList)) {
            return Lists.newArrayList(defaultValue);
        }
        return sourceList;
    }

    public static <K,V> Map<K,V> defaultMap(Map<K,V> map) {
        if(Objects.isNull(map)) {
            return new HashMap<>(0);
        }
        return map;
    }

    public static <K,V> boolean containsKey(Map<K,V> map, K k) {
        if(MapUtils.isEmpty(map)) {
            return false;
        }
        return map.containsKey(k);
    }

    public static <K,V> V getMapValue(Map<K,V> map, K k) {
        if(MapUtils.isEmpty(map)) {
            return null;
        }
        return map.get(k);
    }

    /**
     * 根据条件获取执行值
     * @param condition
     * @param supplier
     * @param <V>
     * @return
     */
    public static <V> V getIfNecessary(boolean condition, Supplier<V> supplier, V defaultValue) {
        if(!condition) {
            return defaultValue;
        }
        return ObjectUtils.defaultIfNull(supplier.get(), defaultValue);
    }

    /**
     * 获取接口上的泛型T
     * @param mapperClass
     * @return
     */
    public static Class<?> getEntityClass(Class<?> mapperClass, Class<?> assignableClass) {
        Type[] interfaceTypes = mapperClass.getGenericInterfaces();
        ParameterizedType parameterizedType = null;
        for(Type interfaceType : interfaceTypes){
            if(!(interfaceType instanceof ParameterizedType)){
                continue;
            }
            ParameterizedType tempParameterizedType = (ParameterizedType)interfaceType;
            if(((Class<?>)tempParameterizedType.getRawType()).isAssignableFrom(assignableClass)){
                parameterizedType = tempParameterizedType;
                break;
            }
        }
        Type type = parameterizedType.getActualTypeArguments()[0];
        return (Class<?>)type;
    }

    /**
     * 获取编码前缀
     * @param code
     * @return
     */
    public static String getCodePrefix(String code) {
        if(StringUtils.isBlank(code)) {
            return code;
        }
        StringBuffer sb = new StringBuffer();
        char[] charArray = code.toCharArray();
        for(char c : charArray) {
            byte b = (byte)c;
            //如果遇到非大写字母则停止遍历
            if (b < 65 || b > 90) {
                break;
            }
            sb.append(c);
        }
        return sb.toString();
    }

    /**
     * 获取定制SQL
     * @param alias
     * @param fieldNameList
     * @return
     */
    public static String getCustomSql(String alias, List<String> fieldNameList) {
        if(CollectionUtils.isEmpty(fieldNameList)) {
            return null;
        }
        StringBuffer sb = new StringBuffer();
        Iterator<String> iterator = fieldNameList.iterator();
        while(iterator.hasNext()) {
            if(StringUtils.isNotBlank(alias)) {
                sb.append(StringUtils.contains(alias,".") ? alias : alias +".");
            }
            sb.append(iterator.next());
            if(iterator.hasNext()) {
                sb.append(",");
            }
        }
        return sb.toString();
    }

    /**
     * 获取简短的UUID
     * @return
     */
    public static String getShortUuid() {
        StringBuffer shortBuffer = new StringBuffer();
        String uuid = UUID.randomUUID().toString().replace("-", "");
        int[] digit = {8,4,16};
        for (int i = 0; i < digit[0]; i++) {
            String str = uuid.substring(i * digit[1], i * digit[1] + digit[1]);
            int x = Integer.parseInt(str, digit[2]);
            shortBuffer.append(CHARS[x % 0x3E]);
        }
        return shortBuffer.toString();
    }

    /**
     * 字符串默认解析成JSONObject
     * @param str
     * @return
     */
    public static JSONObject defaultParseObject(String str) {
        if(StringUtils.isBlank(str)) {
            return new JSONObject(0);
        }
        JSONObject jsonObject = JSONObject.parseObject(str);
        if(jsonObject == null) {
            return new JSONObject(0);
        }
        return jsonObject;
    }

    /**
     * 两个List转成Map
     * @param <K>
     * @param <V>
     * @param keys
     * @param values
     * @return
     */
    public static <K,V> Map<K, V> listToMap(List<K> keys, List<V> values) {
        List<K> newKeys = defaultList(keys);
        return newKeys.stream().collect(HashMap::new, (n, k) -> {
            if(CollectionUtils.isEmpty(values)) {
                n.put(k, null);
            }
            int index = newKeys.indexOf(k);
            if(index >= values.size()) {
                n.put(k, null);
            } else {
                n.put(k, values.get(index));
            }
        }, Map::putAll);
    }

    /**
     * 分区List
     * @param parentList
     * @param size
     * @param queryFunction
     * @param <P>
     * @param <R>
     * @return
     */
    public static <P,R> List<R> partitionList(List<P> parentList, int size, Function<List<P>, List<R>> queryFunction) {
        if(CollectionUtils.isEmpty(parentList)) {
            return null;
        }
        List<List<R>> allList = Lists.partition(parentList, size).stream()
                .map(subList -> queryFunction.apply(subList))
                .filter(c -> CollectionUtils.isNotEmpty(c))
                .collect(Collectors.toList());
        return mergeList(allList);
    }

    /**
     * 合并List
     * @param allList
     * @param <T>
     * @return
     */
    public static <T> List<T> mergeList(List<List<T>> allList) {
        if(CollectionUtils.isEmpty(allList)) {
            return null;
        }
        return allList.stream().collect(ArrayList::new, (parent, sub) -> {
            if(CollectionUtils.isNotEmpty(sub)) {
                parent.addAll(sub);
            }
        }, List::addAll);
    }

    /**
     * 获取消息Key
     * @param billCode
     * @return
     */
    public static String getMessageKey(String billCode) {
        if(StringUtils.isBlank(billCode)) {
            return UUID.randomUUID().toString();
        }
        return StringUtils.joinWith("_", billCode, getShortUuid());
    }

    /**
     * 判断是否是空字符
     * @param object
     * @return
     */
    public static boolean isBlankIfStr(boolean isArray, Object object) {
        if (Objects.isNull(object)) {
            return true;
        } else if(object instanceof CharSequence) {
            return isArray ? isEmptyArray(object.toString()) : StringUtils.isBlank((CharSequence)object);
        } else {
            return false;
        }
    }

    /**
     * 拼接字符串数组
     * @param separator
     * @param arrayStr
     * @return
     */
    public static String joinWith(String separator, String... arrayStr) {
        List<String> list = Arrays.stream(arrayStr).filter(e -> StringUtils.isNotBlank(e)).collect(Collectors.toList());
        return StringUtils.join(list, separator);
    }

    /**
     * 按需提供
     * @param condition
     * @param executeSupplier
     * @param <T>
     * @return
     */
    public static <T> T supplyIfNecessary(boolean condition, Supplier<T> executeSupplier) {
        if(condition) {
            return executeSupplier.get();
        }
        return null;
    }

    /**
     * 获取文件名前缀
     * @param fileName
     * @return
     */
    public static String getFileNamePrefix(String fileName) {
        if(StringUtils.isBlank(fileName)) {
            return "";
        }
        return fileName.substring(0, fileName.lastIndexOf("."));
    }

    /**
     * 获取文件名后缀
     * @param fileName
     * @return
     */
    public static String getFileNameSubfix(String fileName) {
        if(StringUtils.isBlank(fileName)) {
            return "";
        }
        return fileName.substring(fileName.lastIndexOf("."), fileName.length());
    }

    /**
     *  获取图片流数据
     * @param imageUrl
     * @return
     */
    public static byte[] getImageData(final String imageUrl){
        CloseableHttpClient httpClient = HttpClientBuilder.create().build();
        HttpGet httpGet = new HttpGet(imageUrl);
        CloseableHttpResponse resp = null;
        try {
            // 调用服务器接口
            resp = httpClient.execute(httpGet);
            // 将图片或者文件转化成数组的形式
            byte[] data = EntityUtils.toByteArray(resp.getEntity());
            return data;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Map是否同时含有多个key
     * @param map
     * @param keys
     * @return
     */
    public static boolean containsKeys(Map<String, Object> map, String... keys) {
        for(String key : keys) {
            if(!containsKey(map, key)) {
                return false;
            }
        }
        return true;
    }

    /**
     * @param multipartFile
     * @return
     */
    @SneakyThrows
    public static String getFileContent(MultipartFile multipartFile) {
        return IOUtils.toString(multipartFile.getInputStream(), "UTF-8");
    }

    /**
     * 加密
     * @param content
     * @param password
     * @return
     */
    public static String encrypt(String content, String password) {
        if (StringUtils.isBlank(content)) {
            return "";
        }
        BigInteger biginteger = new BigInteger(content.getBytes());
        BigInteger biginteger1 = new BigInteger(password);
        BigInteger biginteger2 = biginteger1.xor(biginteger);
        return biginteger2.toString(32);
    }

    /**
     * 解密
     * @param content
     * @param password
     * @return
     */
    public static String decrypt(String content, String password) {
        if (StringUtils.isBlank(content)) {
            return "";
        }
        BigInteger biginteger = new BigInteger(password);
        BigInteger biginteger1 = new BigInteger(content, 32);
        BigInteger biginteger2 = biginteger1.xor(biginteger);
        return new String(biginteger2.toByteArray());
    }

    /**
     * 判断金额是否大于0
     * @param amount
     * @return
     */
    public static boolean greaterThanZero(BigDecimal amount) {
        return Objects.nonNull(amount) && amount.compareTo(BigDecimal.ZERO) == 1;
    }


    /**
     * 字符串转BigDecimal类型
     *
     * @param value
     * @return
     */
    public static BigDecimal strToBigDecimal(String value) {
        if (StringUtils.isEmpty(value)) {
            return null;
        }
        value = value.replaceAll(REGX, "").replaceAll("¥","").replaceAll(",","");
        return new BigDecimal(value);
    }

    /**
     * list转Map
     * @param keys
     * @param defaultValue
     * @param <K>
     * @param <V>
     * @return
     */
    public static <K, V> Map<K, V> toMap(Collection<K> keys, V defaultValue) {
        Map<K, V> map = new HashMap<>(keys.size());
        keys.forEach(key -> {
            map.putIfAbsent(key, defaultValue);
        });
        return map;
    }

    /**
     * 初始化Map
     * @param key
     * @param defaultValue
     * @param <K>
     * @param <V>
     * @return
     */
    public static <K, V> Map<K, V> initMap(K key, V defaultValue) {
        Map<K, V> map = new HashMap<>(1);
        map.put(key, defaultValue);
        return map;
    }

    /**
     * 获取String
     * @param map
     * @param key
     * @param function
     * @return
     */
    public static <K, V> String defaultString(Map<K, V> map, K key, Function<V, String> function) {
        if(MapUtils.isNotEmpty(map) && Objects.nonNull((key)) && Objects.nonNull(function)) {
            V object = map.get(key);
            if(map.containsKey(key)) {
                return StringUtils.defaultString(function.apply(object));
            }
        }
        return "";
    }


    /**
     * 获取String
     * @param object
     * @param function
     * @param <T>
     * @return
     */
    public static <T> String defaultString(T object, Function<T, String> function) {
        if(Objects.nonNull(object) && Objects.nonNull(function)) {
            return StringUtils.defaultString(function.apply(object));
        }
        return "";
    }

    /**
     * 替换括号（英文转中文）
     * @param input
     * @return
     */
    public static String replaceBrackets(String input) {
        if(StringUtils.isBlank(input)) {
            return input;
        }
        return StringUtils.trim(input).replace("(", "（").replace(")", "）");
    }

    /**
     * 获取自旋重试对象
     * @param supplier
     * @param sleepMills
     * @param maxRetry
     * @param current
     * @param <T>
     * @return
     */
    public static <T> T getRetryObject(Supplier<T> supplier, int sleepMills, int maxRetry, int current) {
        T t = supplier.get();
        if (Objects.isNull(t) && current < maxRetry) {
            try {
                Thread.sleep(sleepMills);
            } catch (InterruptedException e) {
                if (Thread.currentThread().isInterrupted()) {
                    Thread.currentThread().interrupt();
                }
            } finally {
                t = getRetryObject(supplier, sleepMills, maxRetry, ++current);
            }
        }
        return t;
    }
}